/*
 * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.love.cloud.common.security.component;

import cn.hutool.extra.spring.SpringUtil;
import com.love.cloud.common.security.annotation.AnonymousAccess;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 支持remoteTokenServices 负载均衡 2. 支持 获取用户全部信息 3. 接口对外暴露，不校验 Authentication Header 头
 */
@Slf4j
public class MyResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

	@Autowired
	protected ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;

	@Autowired
	protected MyResourceServerTokenServices resourceServerTokenServices;

	@Autowired
	private AccessDeniedHandler pigAccessDeniedHandler;

	@Autowired
	private PermitAllUrlProperties permitAllUrl;

	@Autowired
	private RestTemplate lbRestTemplate;

	@Autowired
	private MyBearerTokenExtractor myBearerTokenExtractor;
	@Resource
	private ApplicationContext applicationContext;

	/**
	 * 默认的配置，对外暴露
	 * @param httpSecurity
	 */
	@Override
	@SneakyThrows
	public void configure(HttpSecurity httpSecurity) {
		// 搜寻匿名标记 url： @AnonymousAccess，然后放行调
		Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
		Set<String> anonymousUrls = new HashSet<>();
		for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
			HandlerMethod handlerMethod = infoEntry.getValue();
			AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
			if (null != anonymousAccess) {
				anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
			}
		}
		// 允许使用iframe 嵌套，避免swagger-ui 不被加载的问题
		httpSecurity.headers().frameOptions().disable();
		ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
				.authorizeRequests();
		permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
		// 自定义匿名访问所有url放行 ： 允许匿名和带权限以及登录用户访问
		registry.antMatchers(anonymousUrls.toArray(new String[0])).permitAll()
				.anyRequest().authenticated().and().csrf().disable();
	}

	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
		RemoteTokenServices remoteTokenServices= SpringUtil.getBean(RemoteTokenServices.class);
		if(remoteTokenServices!=null){
			DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
			UserAuthenticationConverter userTokenConverter = new MyUserAuthenticationConverter();
			accessTokenConverter.setUserTokenConverter(userTokenConverter);

			remoteTokenServices.setRestTemplate(lbRestTemplate);
			remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
			resourceServerTokenServices.setRemoteTokenServices(remoteTokenServices);
		}
		resources.authenticationEntryPoint(resourceAuthExceptionEntryPoint).tokenExtractor(myBearerTokenExtractor)
				.accessDeniedHandler(pigAccessDeniedHandler).tokenServices(resourceServerTokenServices);
	}

}
